module spi_top
(
//8-bit 8051 SFR bus
  clk_i,
  rst_i,
  cs_i,
  wr_i,
  rd_i,
  read_addr_i,	  //independent address bus for single
  write_addr_i,  //clock read-modify-write operations.
  data_in,		
  data_out,	  //read combinational ( unregistered )
  irq_o,

//spi port
  sck_o,		 //serial clock output
  mosi_o,		 //Master out slave in
  miso_i		 //Master in slave out
);

//8-bit 8051 sfr bus
input		   clk_i;
input		   rst_i;
input		   cs_i;        
input		   wr_i;
input		   rd_i;
input    [1:0] read_addr_i;	
input    [1:0] write_addr_i;
input    [7:0] data_in;		
output   [7:0] data_out;	
output		   irq_o;

reg		 [7:0] data_out;

//spi port
output 	   	   sck_o;
output         mosi_o;
input		   miso_i;

reg			  sck_tmp;

//Registers 
wire	 [7:0] rxr;		//receive register

wire 	 [7:0] cr;		//control register
wire 	 [7:0] sr;		//status register
wire	 [7:0] prer;	//prescale register

wire 	rst = rst_i;
wire	clk = clk_i;

//Control register
wire 	 	   enable;	//spi enable
wire		   ien;		//irq enbale
wire		   cpha;	//clock phase
wire		   cpol;	//clock polarity
wire	 [1:0] icnt;	//interrupt count, determine the width transferd

//Status register
reg 		   irq;		//transfer irq
reg 		   wcol;	//write collision
wire		   busy;	//bus busy
wire		   wffull;	//write fifo full
wire 		   wfempty;	//write fifo empty
wire 		   rffull;	//read fifo full
wire 		   rfempty;	//read fifo empty

wire		   wcol_en;


//register write enable signal
wire we_prer 	    =   cs_i & wr_i & ( write_addr_i == 2'd0 ) & !enable; 
wire we_cr 		    =   cs_i & wr_i & ( write_addr_i == 2'd1 );
wire we_sr			=   cs_i & wr_i & ( write_addr_i == 2'd2 );
wire we_txr 	    =   cs_i & wr_i & ( write_addr_i == 2'd3 );

//register read signal
wire read_rxr		=	cs_i & rd_i & ( read_addr_i == 2'd3 );

reg  	 [7:0] clk_cnt;
wire	 	   clk_en;

//Prescale register
spi_register_asyn #(8,8'hFF) PRER_REG
(	.data_in(data_in[7:0]),
	.data_out(prer),
	.we(we_prer),
	.clk(clk),
	.rst(rst)
);

//Control register
spi_register_asyn #(2,0) CONTROL_REG
(	.data_in(data_in[7:6]),
	.data_out(cr[7:6]),
	.we(we_cr),
	.clk(clk),
	.rst(rst)
);

spi_register_asyn #(2,0) CLK_SET
(	.data_in(data_in[5:4]),
	.data_out(cr[5:4]),
	.we(we_cr & !enable),
	.clk(clk),
	.rst(rst)
);

spi_register_asyn #(2,0) ICNT_REG 
(	.data_in(data_in[1:0]),
	.data_out(cr[1:0]),
	.we(we_cr),
	.clk(clk),
	.rst(rst)
);

assign enable		= cr[7];
assign ien			= cr[6];
assign cpha			= cr[5];
assign cpol			= cr[4];
assign icnt			= cr[1:0];
assign cr[3:2]		= 2'b0;

//status register
wire		  irq_en;
always @ (posedge clk, posedge rst)
begin
  if (rst)
 	irq <= 1'b0;
  else if (!enable)
    irq <= 1'b0;
  else if (irq_en)
  	irq <= 1'b1;
  else if (we_sr)
  	irq <= data_in[7];
end

assign wcol_en = we_txr & wffull;

always @ (posedge clk, posedge rst)
begin
  if(rst)
    wcol <= 1'b0;
  else if(!enable)
  	wcol <= 1'b0;
  else if (wcol_en)
    wcol <= 1'b1;
  else if (we_sr)
    wcol <= data_in[7];
end

assign sr = {irq,wcol,1'b0,busy,wffull,wfempty,rffull,rfempty};  

//transmit & receive FIFO
wire	[7:0] tx_fifo_out;
wire	[7:0] rx_fifo_in; 

reg			  read_tx_fifo;
reg			  write_rx_fifo;

spi_fifo TX_FIFO
(	.clk(clk)        ,
	.rst(rst)        ,
	.clr(!enable)    ,
	.data_in(data_in),
	.data_out(tx_fifo_out),
	.write(we_txr & !wffull),			//overrun write won't write
	.read(read_tx_fifo),
	.full(wffull)	 ,
	.empty(wfempty)	 
);

spi_fifo RX_FIFO
(	.clk(clk)        ,
	.rst(rst)        ,
	.clr(!enable)    ,
	.data_in(rx_fifo_in),
	.data_out(rxr)   ,
	.write(write_rx_fifo),				//overrun write will overwrite oldest data
	.read(read_rxr)	 ,
	.full(rffull)	 ,
	.empty(rfempty)	 
);	

//state machine
parameter IDLE	  = 2'b00;
parameter PHASE_A = 2'b01;
parameter PHASE_B = 2'b11; 

reg		[1:0] c_state;
reg		[1:0] next_state;

wire 		  ready_to_transfer;
reg 	[2:0] bit_cnt;
reg		[7:0] shifter;
reg			  tmp_in;

//read register
always @(*)
begin
  if(cs_i & rd_i)
    case(read_addr_i)
	  2'd0   : data_out = prer;
	  2'd1   : data_out = cr;
	  2'd2   : data_out = sr;
	  2'd3   : data_out = rxr;
	  default : data_out = 8'd0;
	endcase
  else
  	data_out = 8'd0;
end

//generate clock for state machine 
always @ (posedge clk, posedge rst)
begin
  if(rst)
 	clk_cnt <= #1 8'hFF;
  else if(!enable)
  	clk_cnt <= #1 prer;
  else if(enable & |clk_cnt & |c_state)
  	clk_cnt <= #1 clk_cnt - 8'd1;
  else if (~|clk_cnt)
  	clk_cnt <= #1 prer;
end
 	 	
assign clk_en = ~|clk_cnt;
assign	ready_to_transfer = !wfempty; 

always @(posedge clk, posedge rst)
begin
  if(rst)
  	c_state <= #1 IDLE;
  else if (!enable)
  	c_state <= #1 IDLE;
  else if((clk_en & |c_state) | (ready_to_transfer & ~|c_state))
  	c_state <= #1 next_state;		//when IDLE, clk_en is always 0. so need use ready_to_transfer to enable clk_cnt.
end

always @(*)
begin
  next_state = IDLE;
  if(enable)
  begin
    case (c_state)
    IDLE:
		if(ready_to_transfer)
	 	  next_state = PHASE_A;
		else
		  next_state = IDLE;
	PHASE_A:
 		  next_state = PHASE_B;
	PHASE_B:
		if(wfempty & ~|bit_cnt )	//transfer is done: tx_fifo empty and this byte is done
		  next_state = IDLE;
		else
		  next_state = PHASE_A;
	default:
		  next_state = IDLE;
	endcase
  end
  else    next_state = IDLE;
end

always @ (posedge clk, posedge rst)
begin
  if(rst)
  begin
    shifter <= #1 8'hFF;
	bit_cnt <= #1 8'd7;
	tmp_in <= #1 1'b0;
	read_tx_fifo <= #1 1'b0;
	write_rx_fifo <= #1 1'b0;
	sck_tmp <= #1 1'b1;
  end
  else if (!enable)
  begin
    shifter <= #1 8'hFF;
	bit_cnt <= #1 8'd7;
	tmp_in <= #1 1'b0;
	read_tx_fifo <= #1 1'b0;
	write_rx_fifo <= #1 1'b0;
	sck_tmp <= #1 1'b1;
  end
  else
  begin
    read_tx_fifo <= #1 1'b0;
	write_rx_fifo<= #1 1'b0;

    case (next_state)
	IDLE:
		if(~|c_state | (|c_state)&clk_en)	//if not in idle, need wait for clk_en
		begin
		  sck_tmp <= #1 cpol;				
		  bit_cnt <= #1 8'd7;
		end
 	PHASE_A:
		begin
		  if( (~|c_state & ready_to_transfer) | (clk_en &c_state & ready_to_transfer & ~|bit_cnt )	)	//idle -> phase_a or phase_b -> phase_a, and first bit
		  begin
		    sck_tmp <= #1 cpha ? ~cpol : cpol; 
		  	shifter <= #1 tx_fifo_out;
			read_tx_fifo <= #1 1;
			bit_cnt <= #1 8'd7;
		  end
		  else if (clk_en)
		  begin
		    sck_tmp <= #1 cpha ? ~cpol : cpol;
		    shifter <= #1 {shifter[6:0],tmp_in};	//shift in the begining of phase_a
			bit_cnt <= #1 bit_cnt - 8'd1;
		  end
		end
	PHASE_B:
		if(clk_en)
		begin
		  sck_tmp <= #1 cpha ? cpol : ~cpol ;
		  if(~|bit_cnt)			//last bit transfered, need to load into rx_fifo
		  begin
		    write_rx_fifo <= #1 1;
		  end
		  else
		    tmp_in <= #1 miso_i;
		end
	endcase
  end
end

assign rx_fifo_in = {shifter[6:0],miso_i};
assign mosi_o = enable? shifter[7] : 1'b1;
assign busy = |c_state;
assign sck_o = enable ? sck_tmp : 1'b1;

//irq singal generate
reg		[1:0] byte_cnt;
wire	byte_done;

assign byte_done = (~|bit_cnt) & clk_en & (c_state == 2'b11);

always @ (posedge clk, posedge rst)
begin
  if(rst)
  byte_cnt <= #1 2'b0;
  else if(!enable)
  byte_cnt <= #1 2'b0;
  else if(byte_done)	
	if(byte_cnt == icnt)	
	  byte_cnt <= #1 0;
	else
	  byte_cnt <= #1 byte_cnt + 2'b1;
end

assign 	irq_en = ( icnt == byte_cnt ) & byte_done;	
assign  irq_o = ien & (irq | wcol);

endmodule

